#include "translator.h"


void Translator::translate(){
	statements.clear();
	//declare
	stringstream ss;
	ss << "#include <stdio.h>"<<endl;
	ss << "#define WriteLine() printf(\"\\n\");" <<endl;
	ss << "#define WriteLong(x) printf(\" %lld\", (long)x);" <<endl;
	ss << "#define ReadLong(a) if (fscanf(stdin, \"%lld\", &a) != 1) a = 0;" <<endl;
	ss << "#define long long long" << endl;
	ss << "#define _push(a) _sp = _sp - 1; *(_sp) = (a);" << endl;
	ss << "#define _pop(a) _sp = _sp + (a);" << endl;
	ss << "#define _alloc(a) _sp = _sp - (a);" << endl;

	ss << "long _global_var[32768/8];"<<endl;
	ss << "long * _gp = _global_var;" << endl;
	ss << "long reg[" << is.size() << "];" <<endl;
	ss << "long stack[4096];" << endl;
	ss << "long * _sp = stack + 4096;" << endl;
	ss << "long * _fp = 0;" << endl<<endl;

	statements.push_back(ss.str());

	int entry = 1;
	int end;
	while(entry < is.size() && translateFunction(entry, end)){
		entry = end + 1;
	}

}

void Translator::output(ostream& o){
	for(int i = 0 ; i < statements.size(); i++){
		o<<statements[i];
	}
}

bool Translator::translateFunction(int& entry, int& end){
	bool isMain = false;
	int nParam = 0; //num of parameters
	int nVar = 0; //num of local variables

	int i = entry;
	while(i < is.size() && is[i].opt != ENTER){
		if(is[i].opt == ENTRYPC)
			isMain = true;
		i++;
	}

	if(i < is.size()){
		nVar = is[i].param1.value/8;
		entry = i;
	}
	else
		return false;

	while(i < is.size() && is[i].opt != RET){
		i++;
	}

	if(i < is.size()){
		nParam = is[i].param1.value/8;
		end = i;
	}
	else
		return false;

	for(i = entry; i <= end; i++){
		translateStatement(i, isMain, nVar, nParam);
	}
	return true;
}
//{UNDEF, NOP, ADD, SUB, MUL, DIV, MOD, NEG, CMPEQ, CMPLE, CMPLT, BR, BLBC, BLBS, CALL, LOAD, STORE, MOVE, READ, WRITE, WRL, PARAM, ENTER, RET, ENTRYPC};
void Translator::translateStatement(int idx, bool isMain, int nVar, int nParam){
	stringstream ss;
	if(idx >= 1 && idx < is.size()){
		Instr instr = is[idx];
		ss << "reg[" << idx << "]=";
		string reg = ss.str();
		ss.str("");
		ss << "label_" << idx <<":";

		string p1, p2;
		translateParam(instr.param1, p1);
		translateParam(instr.param2, p2);
		switch(instr.opt){
			case ADD:
				ss << reg;
				ss << "(long)(" << p1 << ")+(long)(" << p2 <<");\n";
				break;
			case SUB:
				ss << reg;
				ss << "(long)(" << p1 << ")-(long)(" << p2 <<");\n";
				break;
			case MUL:
				ss << reg;
				ss << "(long)(" << p1 << ")*(long)(" << p2 <<");\n";
				break;
			case DIV:
				ss << reg;
				ss << "(long)(" << p1 << ")/(long)(" << p2 <<");\n";
				break;
			case MOD:
				ss << reg;
				ss << "(long)(" << p1 << ")%(long)(" << p2 <<");\n";
				break;
			case NEG:
				ss << reg;
				ss << "-(long)(" << p1<<");\n";
				break;
			case CMPEQ:
				ss << reg;
				ss << "(long)(" << p1 << ")==(long)(" << p2 <<");\n";
				break;
			case CMPLE:
				ss << reg;
				ss << "(long)(" << p1 << ")<=(long)(" << p2 <<");\n";
				break;
			case CMPLT:
				ss << reg;
				ss << "(long)(" << p1 << ")<(long)(" << p2 <<");\n";
				break;
			case BR:
				ss << "goto " << p1<<";\n";
				break;
			case BLBC:
				ss << "if((" << p1 << ")==0) goto " << p2<<";\n";
				break;
			case BLBS:
				ss << "if((" << p1 << ")!=0) goto " << p2<<";\n";
				break;
			case CALL:
				ss << "_push(0);\n";//LNK?
				ss << "_push(_fp);\n";//save previous fp
				ss << "function_" << instr.param1.value << "();\n";
				break;
			case LOAD:
				ss << reg;
				ss << "*((long *)(" << p1 << "));\n";
				break;
			case STORE:
				ss << "*((long *)(" << p2 << ")) = (long)(" << p1<<");\n";
				break;
			case MOVE:
				ss << p2 << "=(long)(" << p1 <<");\n";
				ss << reg;
				ss << "(long)(" << p2 << ");\n";
				break;
			case READ:
				ss << "ReadLong(reg[" << idx << "]);\n";
				break;
			case WRITE:
				ss << "WriteLong((long)(" << p1 << "));\n";
				break;
			case WRL:
				ss << "WriteLine();\n";
				break;
			case PARAM:
				ss << "_push((long)(" << p1 << "));\n";
				break;
			case ENTER:
				ss.str("");
				if(isMain)
					ss << "void main(){\n";
				else
					ss << "void function_" << idx << "(){\n";
				ss << "_fp = _sp;\n";
				ss << "_alloc(" << nVar << ");\n";
				break;
			case RET:
				ss << "_fp = (long *)*(_fp);\n";
				ss << "_pop(" << (nVar + nParam + 2) << ");\n";
				ss << "return;\n}\n";
				break;
			default: return;
		}
		statements.push_back(ss.str());
	}
}
//{NA, GP, FP, CONST, ADDR_OFFSET, FIELD_OFFSET, LOCAL_VAR, REG, LABEL};
void Translator::translateParam(Param& p, string& cr){
	stringstream ss;
	switch(p.type){
		case NA: cr = "";break;
		case GP: cr = "_gp";break;
		case FP: cr = "_fp";break;
		case CONST: ss << p.value; cr = ss.str();break;
		case ADDR_OFFSET: ss << (p.value); cr = ss.str();break;
		case FIELD_OFFSET: ss << (p.value); cr = ss.str();break;
		case LOCAL_VAR: ss << "(*(_fp + " << (p.value/8) << "))"; cr = ss.str(); break;
		case REG: ss << "reg[" << p.value << "]"; cr = ss.str();break;
		case LABEL: ss << "label_" << p.value; cr = ss.str();break;
	}
}


